#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Betaflight多次飞行记录批量分析工具
支持分析多个飞行记录文件，进行对比分析和趋势分析
"""

import os
import glob
import numpy as np
import matplotlib.pyplot as plt
from orangebox import Parser
import matplotlib.font_manager as fm
from datetime import datetime
import pandas as pd

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

class MultiFlightAnalyzer:
    def __init__(self, log_directory):
        self.log_directory = log_directory
        self.flight_data = {}
        self.flight_stats = {}
        
    def find_all_logs(self):
        """查找目录下所有的.bbl文件"""
        pattern = os.path.join(self.log_directory, "*.bbl")
        log_files = glob.glob(pattern)
        log_files.sort()  # 按文件名排序
        return log_files
    
    def extract_flight_data(self, log_file):
        """提取单个飞行记录的数据"""
        try:
            parser = Parser.load(log_file)
            field_names = parser.field_names
            
            # 初始化数据字典
            data = {field: [] for field in field_names}
            
            # 提取数据
            for frame in parser.frames():
                if frame.data is not None:
                    for i, field in enumerate(field_names):
                        if i < len(frame.data) and frame.data[i] is not None:
                            data[field].append(frame.data[i])
                        else:
                            data[field].append(0)
            
            # 转换为numpy数组
            for field in data:
                data[field] = np.array(data[field])
            
            return data, field_names
            
        except Exception as e:
            print(f"解析文件 {log_file} 时出错: {e}")
            return None, None
    
    def calculate_flight_stats(self, data, flight_name):
        """计算单次飞行的统计数据"""
        stats = {
            'flight_name': flight_name,
            'duration': 0,
            'samples': 0
        }
        
        if 'time' in data and len(data['time']) > 0:
            stats['duration'] = (data['time'][-1] - data['time'][0]) / 1000000  # 转换为秒
            stats['samples'] = len(data['time'])
        
        # PID性能统计
        for axis_idx, axis_name in enumerate(['Roll', 'Pitch', 'Yaw']):
            if f'setpoint[{axis_idx}]' in data and f'gyroADC[{axis_idx}]' in data:
                setpoint = data[f'setpoint[{axis_idx}]']
                actual = data[f'gyroADC[{axis_idx}]']
                
                if len(setpoint) > 0 and len(actual) > 0:
                    error = setpoint - actual
                    stats[f'{axis_name}_rms_error'] = np.sqrt(np.mean(error**2))
                    stats[f'{axis_name}_max_error'] = np.max(np.abs(error))
                    stats[f'{axis_name}_mean_setpoint'] = np.mean(np.abs(setpoint))
                    stats[f'{axis_name}_mean_actual'] = np.mean(np.abs(actual))
        
        # 电机统计
        motor_outputs = []
        for i in range(4):
            if f'motor[{i}]' in data:
                motor_outputs.append(data[f'motor[{i}]'])
        
        if motor_outputs:
            motor_outputs = np.array(motor_outputs)
            stats['motor_mean'] = np.mean(motor_outputs)
            stats['motor_std'] = np.std(motor_outputs)
            stats['motor_balance'] = np.std([np.mean(motor) for motor in motor_outputs])
        
        # 电池统计
        if 'vbatLatest' in data and len(data['vbatLatest']) > 0:
            stats['battery_start_voltage'] = data['vbatLatest'][0] / 100  # 转换为伏特
            stats['battery_end_voltage'] = data['vbatLatest'][-1] / 100
            stats['battery_voltage_drop'] = stats['battery_start_voltage'] - stats['battery_end_voltage']
        
        if 'amperageLatest' in data and len(data['amperageLatest']) > 0:
            current = data['amperageLatest'] / 100  # 转换为安培
            stats['current_mean'] = np.mean(current)
            stats['current_max'] = np.max(current)
        
        return stats
    
    def analyze_all_flights(self):
        """分析所有飞行记录"""
        log_files = self.find_all_logs()
        
        if not log_files:
            print(f"在目录 {self.log_directory} 中没有找到.bbl文件")
            return
        
        print(f"找到 {len(log_files)} 个飞行记录文件")
        
        for log_file in log_files:
            flight_name = os.path.splitext(os.path.basename(log_file))[0]
            print(f"\n分析飞行记录: {flight_name}")
            
            data, field_names = self.extract_flight_data(log_file)
            if data is not None:
                self.flight_data[flight_name] = data
                stats = self.calculate_flight_stats(data, flight_name)
                self.flight_stats[flight_name] = stats
                
                print(f"  飞行时长: {stats.get('duration', 0):.1f}秒")
                print(f"  数据采样点: {stats.get('samples', 0)}个")
    
    def create_comparison_charts(self, output_dir):
        """创建多次飞行对比图表"""
        os.makedirs(output_dir, exist_ok=True)
        
        if len(self.flight_data) < 2:
            print("需要至少2次飞行记录才能进行对比分析")
            return
        
        # 1. PID性能对比图
        self.plot_pid_performance_comparison(output_dir)
        
        # 2. 电机平衡对比图
        self.plot_motor_balance_comparison(output_dir)
        
        # 3. 电池性能对比图
        self.plot_battery_performance_comparison(output_dir)
        
        # 4. 飞行时长和强度对比
        self.plot_flight_duration_comparison(output_dir)
        
        # 5. 趋势分析图
        self.plot_performance_trends(output_dir)
    
    def plot_pid_performance_comparison(self, output_dir):
        """绘制PID性能对比图"""
        fig, axes = plt.subplots(2, 3, figsize=(15, 10))
        fig.suptitle('多次飞行PID性能对比', fontsize=16, fontweight='bold')
        
        axes_names = ['Roll', 'Pitch', 'Yaw']
        metrics = ['rms_error', 'max_error']
        
        flight_names = list(self.flight_stats.keys())
        
        for metric_idx, metric in enumerate(metrics):
            for axis_idx, axis_name in enumerate(axes_names):
                ax = axes[metric_idx, axis_idx]
                
                values = []
                labels = []
                
                for flight_name in flight_names:
                    stats = self.flight_stats[flight_name]
                    key = f'{axis_name}_{metric}'
                    if key in stats:
                        values.append(stats[key])
                        labels.append(flight_name)
                
                if values:
                    bars = ax.bar(range(len(values)), values, alpha=0.7)
                    ax.set_title(f'{axis_name}轴 {metric.replace("_", " ").title()}')
                    ax.set_xticks(range(len(labels)))
                    ax.set_xticklabels(labels, rotation=45, ha='right')
                    ax.grid(True, alpha=0.3)
                    
                    # 添加数值标签
                    for i, (bar, value) in enumerate(zip(bars, values)):
                        ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + max(values)*0.01,
                               f'{value:.1f}', ha='center', va='bottom', fontsize=8)
        
        plt.tight_layout()
        plt.savefig(os.path.join(output_dir, 'PID性能对比图.png'), dpi=300, bbox_inches='tight')
        plt.close()
        print("✓ 生成PID性能对比图")
    
    def plot_motor_balance_comparison(self, output_dir):
        """绘制电机平衡对比图"""
        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
        fig.suptitle('多次飞行电机性能对比', fontsize=16, fontweight='bold')
        
        flight_names = list(self.flight_stats.keys())
        
        # 电机平衡性对比
        balance_values = []
        mean_values = []
        labels = []
        
        for flight_name in flight_names:
            stats = self.flight_stats[flight_name]
            if 'motor_balance' in stats and 'motor_mean' in stats:
                balance_values.append(stats['motor_balance'])
                mean_values.append(stats['motor_mean'])
                labels.append(flight_name)
        
        if balance_values:
            bars1 = ax1.bar(range(len(balance_values)), balance_values, alpha=0.7, color='orange')
            ax1.set_title('电机输出平衡性\n(数值越小越平衡)')
            ax1.set_xticks(range(len(labels)))
            ax1.set_xticklabels(labels, rotation=45, ha='right')
            ax1.set_ylabel('标准差')
            ax1.grid(True, alpha=0.3)
            
            for bar, value in zip(bars1, balance_values):
                ax1.text(bar.get_x() + bar.get_width()/2, bar.get_height() + max(balance_values)*0.01,
                        f'{value:.1f}', ha='center', va='bottom', fontsize=8)
        
        if mean_values:
            bars2 = ax2.bar(range(len(mean_values)), mean_values, alpha=0.7, color='green')
            ax2.set_title('平均电机输出')
            ax2.set_xticks(range(len(labels)))
            ax2.set_xticklabels(labels, rotation=45, ha='right')
            ax2.set_ylabel('输出值')
            ax2.grid(True, alpha=0.3)
            
            for bar, value in zip(bars2, mean_values):
                ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + max(mean_values)*0.01,
                        f'{value:.0f}', ha='center', va='bottom', fontsize=8)
        
        plt.tight_layout()
        plt.savefig(os.path.join(output_dir, '电机性能对比图.png'), dpi=300, bbox_inches='tight')
        plt.close()
        print("✓ 生成电机性能对比图")
    
    def plot_battery_performance_comparison(self, output_dir):
        """绘制电池性能对比图"""
        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
        fig.suptitle('多次飞行电池性能对比', fontsize=16, fontweight='bold')
        
        flight_names = list(self.flight_stats.keys())
        
        # 电压降对比
        voltage_drop_values = []
        current_mean_values = []
        labels = []
        
        for flight_name in flight_names:
            stats = self.flight_stats[flight_name]
            if 'battery_voltage_drop' in stats:
                voltage_drop_values.append(stats['battery_voltage_drop'])
                labels.append(flight_name)
            if 'current_mean' in stats:
                current_mean_values.append(stats['current_mean'])
        
        if voltage_drop_values:
            bars1 = ax1.bar(range(len(voltage_drop_values)), voltage_drop_values, alpha=0.7, color='red')
            ax1.set_title('电池电压降')
            ax1.set_xticks(range(len(labels)))
            ax1.set_xticklabels(labels, rotation=45, ha='right')
            ax1.set_ylabel('电压降 (V)')
            ax1.grid(True, alpha=0.3)
            
            for bar, value in zip(bars1, voltage_drop_values):
                ax1.text(bar.get_x() + bar.get_width()/2, bar.get_height() + max(voltage_drop_values)*0.01,
                        f'{value:.2f}V', ha='center', va='bottom', fontsize=8)
        
        if current_mean_values:
            bars2 = ax2.bar(range(len(current_mean_values)), current_mean_values, alpha=0.7, color='blue')
            ax2.set_title('平均电流消耗')
            ax2.set_xticks(range(len(labels)))
            ax2.set_xticklabels(labels, rotation=45, ha='right')
            ax2.set_ylabel('电流 (A)')
            ax2.grid(True, alpha=0.3)
            
            for bar, value in zip(bars2, current_mean_values):
                ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + max(current_mean_values)*0.01,
                        f'{value:.1f}A', ha='center', va='bottom', fontsize=8)
        
        plt.tight_layout()
        plt.savefig(os.path.join(output_dir, '电池性能对比图.png'), dpi=300, bbox_inches='tight')
        plt.close()
        print("✓ 生成电池性能对比图")
    
    def plot_flight_duration_comparison(self, output_dir):
        """绘制飞行时长对比图"""
        fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
        fig.suptitle('多次飞行基本信息对比', fontsize=16, fontweight='bold')
        
        flight_names = list(self.flight_stats.keys())
        
        durations = []
        samples = []
        labels = []
        
        for flight_name in flight_names:
            stats = self.flight_stats[flight_name]
            if 'duration' in stats and 'samples' in stats:
                durations.append(stats['duration'])
                samples.append(stats['samples'])
                labels.append(flight_name)
        
        if durations:
            bars1 = ax1.bar(range(len(durations)), durations, alpha=0.7, color='purple')
            ax1.set_title('飞行时长')
            ax1.set_xticks(range(len(labels)))
            ax1.set_xticklabels(labels, rotation=45, ha='right')
            ax1.set_ylabel('时长 (秒)')
            ax1.grid(True, alpha=0.3)
            
            for bar, value in zip(bars1, durations):
                ax1.text(bar.get_x() + bar.get_width()/2, bar.get_height() + max(durations)*0.01,
                        f'{value:.1f}s', ha='center', va='bottom', fontsize=8)
        
        if samples:
            bars2 = ax2.bar(range(len(samples)), samples, alpha=0.7, color='brown')
            ax2.set_title('数据采样点数')
            ax2.set_xticks(range(len(labels)))
            ax2.set_xticklabels(labels, rotation=45, ha='right')
            ax2.set_ylabel('采样点数')
            ax2.grid(True, alpha=0.3)
            
            for bar, value in zip(bars2, samples):
                ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + max(samples)*0.01,
                        f'{value}', ha='center', va='bottom', fontsize=8)
        
        plt.tight_layout()
        plt.savefig(os.path.join(output_dir, '飞行基本信息对比图.png'), dpi=300, bbox_inches='tight')
        plt.close()
        print("✓ 生成飞行基本信息对比图")
    
    def plot_performance_trends(self, output_dir):
        """绘制性能趋势图"""
        if len(self.flight_stats) < 3:
            print("需要至少3次飞行记录才能进行趋势分析")
            return
        
        fig, axes = plt.subplots(2, 2, figsize=(14, 10))
        fig.suptitle('飞行性能趋势分析', fontsize=16, fontweight='bold')
        
        flight_names = list(self.flight_stats.keys())
        x_pos = range(len(flight_names))
        
        # Roll轴RMS误差趋势
        roll_rms = [self.flight_stats[name].get('Roll_rms_error', 0) for name in flight_names]
        axes[0, 0].plot(x_pos, roll_rms, 'o-', linewidth=2, markersize=6, color='red')
        axes[0, 0].set_title('Roll轴RMS误差趋势')
        axes[0, 0].set_xticks(x_pos)
        axes[0, 0].set_xticklabels(flight_names, rotation=45, ha='right')
        axes[0, 0].grid(True, alpha=0.3)
        axes[0, 0].set_ylabel('RMS误差')
        
        # Pitch轴RMS误差趋势
        pitch_rms = [self.flight_stats[name].get('Pitch_rms_error', 0) for name in flight_names]
        axes[0, 1].plot(x_pos, pitch_rms, 'o-', linewidth=2, markersize=6, color='green')
        axes[0, 1].set_title('Pitch轴RMS误差趋势')
        axes[0, 1].set_xticks(x_pos)
        axes[0, 1].set_xticklabels(flight_names, rotation=45, ha='right')
        axes[0, 1].grid(True, alpha=0.3)
        axes[0, 1].set_ylabel('RMS误差')
        
        # 电机平衡性趋势
        motor_balance = [self.flight_stats[name].get('motor_balance', 0) for name in flight_names]
        axes[1, 0].plot(x_pos, motor_balance, 'o-', linewidth=2, markersize=6, color='orange')
        axes[1, 0].set_title('电机平衡性趋势')
        axes[1, 0].set_xticks(x_pos)
        axes[1, 0].set_xticklabels(flight_names, rotation=45, ha='right')
        axes[1, 0].grid(True, alpha=0.3)
        axes[1, 0].set_ylabel('平衡性指标')
        
        # 电池电压降趋势
        voltage_drop = [self.flight_stats[name].get('battery_voltage_drop', 0) for name in flight_names]
        axes[1, 1].plot(x_pos, voltage_drop, 'o-', linewidth=2, markersize=6, color='blue')
        axes[1, 1].set_title('电池电压降趋势')
        axes[1, 1].set_xticks(x_pos)
        axes[1, 1].set_xticklabels(flight_names, rotation=45, ha='right')
        axes[1, 1].grid(True, alpha=0.3)
        axes[1, 1].set_ylabel('电压降 (V)')
        
        plt.tight_layout()
        plt.savefig(os.path.join(output_dir, '性能趋势分析图.png'), dpi=300, bbox_inches='tight')
        plt.close()
        print("✓ 生成性能趋势分析图")
    
    def generate_comparison_report(self, output_dir):
        """生成多次飞行对比报告"""
        report = []
        report.append("# 多次飞行记录对比分析报告")
        report.append("")
        report.append(f"## 概述")
        report.append(f"分析了 **{len(self.flight_stats)}** 次飞行记录")
        report.append("")
        
        # 基本信息表格
        report.append("## 飞行基本信息")
        report.append("")
        report.append("| 飞行记录 | 时长(秒) | 采样点数 | 开始电压(V) | 结束电压(V) | 电压降(V) |")
        report.append("|---------|---------|---------|------------|------------|----------|")
        
        for flight_name, stats in self.flight_stats.items():
            duration = stats.get('duration', 0)
            samples = stats.get('samples', 0)
            start_v = stats.get('battery_start_voltage', 0)
            end_v = stats.get('battery_end_voltage', 0)
            drop_v = stats.get('battery_voltage_drop', 0)
            
            report.append(f"| {flight_name} | {duration:.1f} | {samples} | {start_v:.2f} | {end_v:.2f} | {drop_v:.2f} |")
        
        report.append("")
        
        # PID性能对比
        report.append("## PID性能对比")
        report.append("")
        
        for axis in ['Roll', 'Pitch', 'Yaw']:
            report.append(f"### {axis}轴性能")
            report.append("")
            report.append("| 飞行记录 | RMS误差 | 最大误差 | 平均设定点 | 平均实际值 |")
            report.append("|---------|---------|---------|------------|------------|")
            
            for flight_name, stats in self.flight_stats.items():
                rms = stats.get(f'{axis}_rms_error', 0)
                max_err = stats.get(f'{axis}_max_error', 0)
                mean_sp = stats.get(f'{axis}_mean_setpoint', 0)
                mean_act = stats.get(f'{axis}_mean_actual', 0)
                
                report.append(f"| {flight_name} | {rms:.2f} | {max_err:.2f} | {mean_sp:.2f} | {mean_act:.2f} |")
            
            report.append("")
        
        # 电机性能对比
        report.append("## 电机性能对比")
        report.append("")
        report.append("| 飞行记录 | 平均输出 | 输出标准差 | 平衡性指标 |")
        report.append("|---------|---------|------------|------------|")
        
        for flight_name, stats in self.flight_stats.items():
            mean_motor = stats.get('motor_mean', 0)
            std_motor = stats.get('motor_std', 0)
            balance = stats.get('motor_balance', 0)
            
            report.append(f"| {flight_name} | {mean_motor:.0f} | {std_motor:.1f} | {balance:.1f} |")
        
        report.append("")
        
        # 电池性能对比
        report.append("## 电池性能对比")
        report.append("")
        report.append("| 飞行记录 | 平均电流(A) | 最大电流(A) | 电压降(V) |")
        report.append("|---------|-------------|-------------|----------|")
        
        for flight_name, stats in self.flight_stats.items():
            mean_current = stats.get('current_mean', 0)
            max_current = stats.get('current_max', 0)
            voltage_drop = stats.get('battery_voltage_drop', 0)
            
            report.append(f"| {flight_name} | {mean_current:.1f} | {max_current:.1f} | {voltage_drop:.2f} |")
        
        report.append("")
        
        # 分析结论
        report.append("## 分析结论")
        report.append("")
        
        # 找出最佳和最差的飞行
        if len(self.flight_stats) >= 2:
            # 基于Roll轴RMS误差评估
            roll_errors = {name: stats.get('Roll_rms_error', float('inf')) 
                          for name, stats in self.flight_stats.items()}
            
            best_flight = min(roll_errors.keys(), key=lambda x: roll_errors[x])
            worst_flight = max(roll_errors.keys(), key=lambda x: roll_errors[x])
            
            report.append(f"### 性能评估")
            report.append(f"- **最佳飞行**: {best_flight} (Roll轴RMS误差: {roll_errors[best_flight]:.2f})")
            report.append(f"- **需要改进**: {worst_flight} (Roll轴RMS误差: {roll_errors[worst_flight]:.2f})")
            report.append("")
        
        # 趋势分析
        if len(self.flight_stats) >= 3:
            report.append(f"### 趋势分析")
            
            flight_names = list(self.flight_stats.keys())
            roll_rms_trend = [self.flight_stats[name].get('Roll_rms_error', 0) for name in flight_names]
            
            if len(roll_rms_trend) >= 3:
                # 简单的趋势判断
                recent_avg = np.mean(roll_rms_trend[-2:])
                early_avg = np.mean(roll_rms_trend[:2])
                
                if recent_avg < early_avg:
                    report.append("- **Roll轴性能**: 呈改善趋势 📈")
                elif recent_avg > early_avg:
                    report.append("- **Roll轴性能**: 呈下降趋势 📉")
                else:
                    report.append("- **Roll轴性能**: 保持稳定 ➡️")
            
            report.append("")
        
        # 建议
        report.append("## 优化建议")
        report.append("")
        report.append("### 基于多次飞行数据的建议")
        
        # 分析所有飞行的平均性能
        avg_roll_rms = np.mean([stats.get('Roll_rms_error', 0) for stats in self.flight_stats.values()])
        avg_pitch_rms = np.mean([stats.get('Pitch_rms_error', 0) for stats in self.flight_stats.values()])
        avg_motor_balance = np.mean([stats.get('motor_balance', 0) for stats in self.flight_stats.values()])
        
        if avg_roll_rms > 50:
            report.append("- **Roll轴**: 平均RMS误差较高，建议增加P值或检查机械结构")
        
        if avg_pitch_rms > 50:
            report.append("- **Pitch轴**: 平均RMS误差较高，建议增加P值或检查机械结构")
        
        if avg_motor_balance > 50:
            report.append("- **电机平衡**: 电机输出不够平衡，检查螺旋桨、电机或ESC")
        
        report.append("")
        report.append("### 持续改进建议")
        report.append("- 定期记录飞行数据，建立性能基线")
        report.append("- 对比不同天气条件下的飞行表现")
        report.append("- 记录硬件更换前后的性能变化")
        report.append("- 建立飞行日志，关联飞行条件和性能数据")
        
        # 保存报告
        with open(os.path.join(output_dir, "多次飞行对比分析报告.md"), 'w', encoding='utf-8') as f:
            f.write('\n'.join(report))
        
        print("✓ 生成多次飞行对比分析报告")

def main():
    # 设置日志文件目录
    log_directory = "d:/Git/FlightRecord/SFPV_85/record"
    output_directory = "d:/Git/FlightRecord/SFPV_85/multi_flight_analysis"
    
    # 创建分析器
    analyzer = MultiFlightAnalyzer(log_directory)
    
    # 分析所有飞行记录
    analyzer.analyze_all_flights()
    
    if not analyzer.flight_data:
        print("没有找到有效的飞行记录数据")
        return
    
    # 创建输出目录
    os.makedirs(output_directory, exist_ok=True)
    
    # 生成对比图表
    analyzer.create_comparison_charts(output_directory)
    
    # 生成对比报告
    analyzer.generate_comparison_report(output_directory)
    
    print(f"\n=== 多次飞行分析完成 ===")
    print(f"结果已保存到: {output_directory}")
    print(f"\n分析的飞行记录:")
    for flight_name, stats in analyzer.flight_stats.items():
        duration = stats.get('duration', 0)
        print(f"- {flight_name}: {duration:.1f}秒")
    
    print("\n生成的文件:")
    print("- 多次飞行对比分析报告.md")
    print("- PID性能对比图.png")
    print("- 电机性能对比图.png")
    print("- 电池性能对比图.png")
    print("- 飞行基本信息对比图.png")
    if len(analyzer.flight_stats) >= 3:
        print("- 性能趋势分析图.png")

if __name__ == "__main__":
    main()